package cn.jichangxiu.auth.service;

import cn.jichangxiu.common.constant.Instance;
import cn.jichangxiu.common.ucenter.model.AuthToken;
import cn.jichangxiu.common.web.model.exception.PreyException;
import cn.jichangxiu.common.web.model.response.ResponseCode;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.*;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class AuthService {

    @Value("${auth.tokenValiditySeconds}")
    private int tokenValiditySeconds;

    // http 远程调用操作
    @Autowired
    private RestTemplate restTemplate;

    // redis 操作
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    // 注册中心
    @Autowired
    LoadBalancerClient loadBalancerClient;

    public AuthToken login(String username, String password, String clientId, String clientSecret) throws PreyException {
        //请求spring security申请令牌
        AuthToken authToken = this.applyToken(username, password, clientId, clientSecret);
        if (authToken == null) {
            throw new PreyException(ResponseCode.FAIL);
        }
        //用户身份令牌
        String accessToken = authToken.getAccessToken();
        //存储到redis中的内容
        String jsonString = JSON.toJSONString(authToken);
        //将令牌存储到redis
        boolean result = this.saveToken(accessToken, jsonString, tokenValiditySeconds);
        if (!result) {
            throw new PreyException(ResponseCode.FAIL);
        }
        return authToken;
    }

    /**
     * 将令牌存储到Redis
     *
     * @param accessToken
     * @param content
     * @param time
     * @return
     */
    private boolean saveToken(String accessToken, String content, long time) {
        String key = "accessToken:" + accessToken;
        stringRedisTemplate.boundValueOps(key).set(content, time, TimeUnit.SECONDS);
        Long expire = stringRedisTemplate.getExpire(key, TimeUnit.SECONDS);
        return expire > 0;
    }

    /**
     * 从Redis删除令牌
     *
     * @param accessToken
     * @return
     */
    public boolean delToken(String accessToken) {
        String key = "accessToken:" + accessToken;
        stringRedisTemplate.delete(key);
        return true;
    }

    /**
     * 从Redis删除令牌
     *
     * @param accessToken
     * @return
     */
    public AuthToken getUserToken(String accessToken) {
        String key = "accessToken:" + accessToken;
        //从redis中取到令牌信息
        String value = stringRedisTemplate.opsForValue().get(key);
        //转成对象
        try {
            AuthToken authToken = JSON.parseObject(value, AuthToken.class);
            return authToken;
        } catch (Exception e) {

            return null;
        }
    }


    /**
     * 申请令牌
     *
     * @param username
     * @param password
     * @param clientId
     * @param clientSecret
     * @return
     */
    private AuthToken applyToken(String username, String password, String clientId, String clientSecret) {
        //从eureka中获取认证服务的地址（因为spring security在认证服务中）
        //从eureka中获取认证服务的一个实例的地址
        ServiceInstance serviceInstance = loadBalancerClient.choose(Instance.AUTHSERVER);
        //此地址就是http://ip:port
        URI uri = serviceInstance.getUri();
        //令牌申请的地址 http://localhost:40400/auth/oauth/token
        String authUrl = uri+ "/oauth/token";
        //定义header
        LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>();
        String httpBasic = getHttpBasic(clientId, clientSecret);
        header.add("Authorization",httpBasic);

        //定义body
        LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type","password");
        body.add("username",username);
        body.add("password",password);

        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, header);
        //String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables

        //设置restTemplate远程调用时候，对400和401不让报错，正确返回数据
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                if(response.getRawStatusCode()!=400 && response.getRawStatusCode()!=401){
                    super.handleError(response);
                }
            }
        });

        ResponseEntity<Map> exchange = restTemplate.exchange(authUrl, HttpMethod.POST, httpEntity, Map.class);

        //申请令牌信息
        Map bodyMap = exchange.getBody();
        if(bodyMap == null ||
                bodyMap.get("access_token") == null ||
                bodyMap.get("refresh_token") == null ||
                bodyMap.get("jti") == null){

            //解析spring security返回的错误信息
            if(bodyMap!=null && bodyMap.get("error_description")!=null){
                String error_description = (String) bodyMap.get("error_description");
                if(error_description.indexOf("UserDetailsService returned null")>=0){
                    throw new PreyException(ResponseCode.LOGIN_FALL_ACCOUNT_ERROR);
                }else if(error_description.indexOf("坏的凭证")>=0){
                    throw new PreyException(ResponseCode.LOGIN_FALL_PASSWORD_ERROR);
                }
            }
            return null;
        }
        AuthToken authToken = new AuthToken();
        authToken.setAccessToken((String) bodyMap.get("jti"));//用户身份令牌
        authToken.setRefreshToken((String) bodyMap.get("refresh_token"));//刷新令牌
        authToken.setJwtToken((String) bodyMap.get("access_token"));//jwt令牌
        return authToken;
    }


    //获取httpbasic的串
    private String getHttpBasic(String clientId,String clientSecret){
        String string = clientId + ":" + clientSecret;
        //将串进行base64编码
        byte[] encode = Base64Utils.encode(string.getBytes());
        return "Basic "+ new String(encode);
    }

}
